﻿using Games.Common;
using Games.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Essentials;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Games.TwentyFive
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class GamePage : ContentPage
    {
        IDataBase database = DependencyService.Get<IDataBase>();
        GameInfo gameInfo;
        int cellSize;
        double layoutWidth, layoutHeight;
        public static readonly int LENGTH = 5;
        bool isRunning = false;
        List<CircleLabel> lbls = new List<CircleLabel>();
        TapGestureRecognizer tap = new TapGestureRecognizer { NumberOfTapsRequired = 1 };
        TapGestureRecognizer tapStart = new TapGestureRecognizer { NumberOfTapsRequired = 1 };
        System.Timers.Timer timer;
        internal GamePage(GameInfo gameInfo)
        {
            InitializeComponent();
            TimeCounter.Stop();
            timer = new System.Timers.Timer();
            timer.Interval = 5;
            timer.Elapsed += this.Timer_Elapsed;
            tap.Tapped += this.Tap_Tapped;
            tapStart.Tapped += this.StartClicked;
            BindingContext = this.gameInfo = gameInfo;
            gameInfo.Seconds = "0.000";
            btnStart.GestureRecognizers.Add(tapStart);
            database.CreateTable<TwentyFiveRecord>();
        }
        private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            string time = TimeCounter.Value().ToString("N3");
            gameInfo.Seconds = time;
        }

        void OnLayoutSizeChanged(object sender, EventArgs args)
        {
            Layout layout = sender as Layout;
            layoutWidth = layout.Width;
            layoutHeight = layout.Height;
            InitLayout();
        }

        void InitLayout()
        {
            var sizew = (int)((layoutWidth - 20) / LENGTH);
            var sizeh = (int)((layoutHeight - 20) / LENGTH);
            cellSize = Math.Min(sizew, sizeh);
            var laySize = LENGTH * cellSize;
            board.WidthRequest = board.HeightRequest = laySize + 6;
            lblValue.FontSize = cellSize / 2;
            lblValue.WidthRequest = lblValue.HeightRequest = cellSize;

            var xMargin = (layoutWidth - laySize - 6) / 2.0;
            var yMargin = (layoutHeight - laySize - 6) / 2.0;
            board.Margin = new Thickness(xMargin, yMargin);
            absoluteLayout.WidthRequest = absoluteLayout.HeightRequest = laySize;
            absoluteLayout.Margin = new Thickness(3);
            var count = LENGTH * LENGTH;

            gdctrl.Margin = new Thickness((layoutWidth - 240) / 2, (layoutHeight - 240) / 2);

            while(absoluteLayout.Children.Count < count)
            {
                CircleLabel lifeCell = new CircleLabel();
                lifeCell.BackColor = GetColor();
                lifeCell.TextColor = Color.GhostWhite;
                lifeCell.HorizontalTextAlignment = TextAlignment.Center;
                lifeCell.VerticalTextAlignment = TextAlignment.Center;
                lifeCell.FontAttributes = FontAttributes.Bold;
                lifeCell.FontSize = cellSize / 2;
                lifeCell.GestureRecognizers.Add(tap);
                absoluteLayout.Children.Add(lifeCell);
            }

            for(int i = 0; i < count; i++)
            {
                CircleLabel cell = (CircleLabel)absoluteLayout.Children[i];
                var col = i % LENGTH;
                var row = i / LENGTH;
                //cell.Text = (i + 1).ToString();
                cell.Scale = 0;
                Rectangle rect = new Rectangle(col * cellSize + 1, row * cellSize + 1, cellSize - 2, cellSize - 2);
                AbsoluteLayout.SetLayoutBounds(cell, rect);
            }
            ShowStart();
        }
        private Color GetColor()
        {
            return Utils.GetRandomColor(512);
        }
        async Task InitGame()
        {
            lbls.Clear();
            var list = Init();
            var tasks = new List<Task>();
            var count = LENGTH * LENGTH;
            for(int i = 0; i < count; i++)
            {
                CircleLabel cell = (CircleLabel)absoluteLayout.Children[i];
                Xamarin.Forms.ViewExtensions.CancelAnimations(cell);
                cell.BackColor = GetColor();
                cell.Scale = 0;
            }
            ushort len = 600;
            for(int i = 0; i < count; i++)
            {
                var index = list[i];
                CircleLabel cell = (CircleLabel)absoluteLayout.Children[index];
                lbls.Add(cell);
                cell.Text = (i + 1).ToString();
                tasks.Add(cell.ScaleTo(1, len, Easing.CubicIn));
                await Task.Delay(len / 4);
                len -= 20;
            }
            await Task.WhenAll(tasks);
            TimeCounter.Start();
            timer.Start();
            isRunning = true;
        }

        private void ShowStart()
        {
            timer.Stop();
            gameInfo.Seconds = "0.000";
            djs.IsVisible = false;
            btnStart.BackColor = GetColor();
            lblValue.BackColor = btnStart.BackColor;
            lblValue.Text = "0";
            btnStart.IsVisible = true;
            gdctrl.IsVisible = true;
            btnStart.Show();
        }

        private async Task Start()
        {
            btnStart.IsVisible = false;
            djs.IsVisible = true;
            gdctrl.IsVisible = true;
            for(int i = 3; i > 0; i--)
            {
                await Zoom(i.ToString());
            }
            gdctrl.IsVisible = false;
            await InitGame();
        }

        private async Task Zoom(string text, uint length = 1000)
        {
            djs.Text = text;
            //djs.Opacity = 0.3;
            djs.Scale = 10;
            var tasks = new List<Task>();
            tasks.Add(djs.ScaleTo(0, length, Easing.Linear));
            //tasks.Add(djs.FadeTo(1, length, Easing.CubicIn));
            await Task.WhenAll(tasks);
        }
        private List<int> Init()
        {
            var list = new List<int>();
            var cnt = LENGTH * LENGTH;
            var rd = new Random(DateTime.Now.Millisecond);
            while(list.Count < cnt)
            {
                var n = rd.Next(cnt);
                if(!list.Contains(n))
                {
                    list.Add(n);
                }
            }
            return list;
        }

        async void Tap_Tapped(object sender, EventArgs e)
        {
            if(!isRunning)
            {
                return;
            }
            var cell = sender as CircleLabel;
            var lbl0 = lbls[0];
            if(cell != lbl0)
            {
                Vibration.Vibrate(100);
                return;
            }
            lblValue.Text = cell.Text;
            lblValue.BackColor = cell.BackColor;
            lbls.RemoveAt(0);
            if(lbls.Count == 0)
            {
                isRunning = false;
            }
            var tasks = new List<Task>();
            tasks.Add(cell.ScaleTo(0, 250, Easing.CubicOut));
            if(lbls.Count == 0)
            {
                tasks.Add(ShowFinish());
            }
            await Task.WhenAny(tasks);
        }
        async void StartClicked(object sender, EventArgs args)
        {
            isRunning = false;
            await Start();
        }
        async Task ShowFinish()
        {
            var record = await SaveRecordAsync();
            await DisplayAlert(record.Level, $"{record.Message}", "OK");
            ShowStart();
        }
        async Task<Record> SaveRecordAsync()
        {
            var dur = TimeCounter.StopDouble();
            timer.Stop();
            gameInfo.Seconds = dur.ToString("N3");
            var cRecord = new TwentyFiveRecord()
            {
                GameID = gameInfo.ID,
                CreateTime = DateTime.Now,
                Duration = dur
            };
            await database.SaveRecordByDiff(cRecord, 100,
                null,
                list => list.OrderBy(x => x.Duration).ThenByDescending(x => x.ID),
                o => o.Duration > cRecord.Duration
            );
            return cRecord.Convert();
        }

        void OnAboutButtonClicked(object sender, EventArgs args)
        {
            if(!mask.Contains("about"))
            {
                var aboutView = ControlUtil.GetAbout(gameInfo);
                mask.Add("about", gameInfo.Name, aboutView);
            }
            mask.ShowWithCancel("about", "确定");
        }
        void OnRecordClicked(object sender, EventArgs args)
        {
            //var sorter = new Sorter<TwentyFiveRecord>(q => q.OrderBy(x => x.Duration).ThenByDescending(x => x.ID));
            Navigation.PushAsync(new RecordPage(gameInfo.Name, new RecordViewModel<TwentyFiveRecord>(null)));
        }

        private void TapToBack(object sender, EventArgs e)
        {
            this.timer.Dispose();
            this.timer = null;
            Navigation.PopAsync();
        }
    }
}